#include "QQUtility.h"

QString handle_generate_device_guid()
{
	QString result;
#ifdef Q_OS_WIN
	GUID guid;
	if (CoCreateGuid(&guid) == S_OK) {
		result = QString("%1-%2-%3-%4%5-%6%7%8%9%10%11%12")
			.arg(guid.Data1, 8, 16, QChar('0'))
			.arg(guid.Data2, 4, 16, QChar('0'))
			.arg(guid.Data3, 4, 16, QChar('0'))
			.arg(guid.Data4[0], 2, 16, QChar('0'))
			.arg(guid.Data4[1], 2, 16, QChar('0'))
			.arg(guid.Data4[2], 2, 16, QChar('0'))
			.arg(guid.Data4[3], 2, 16, QChar('0'))
			.arg(guid.Data4[4], 2, 16, QChar('0'))
			.arg(guid.Data4[5], 2, 16, QChar('0'))
			.arg(guid.Data4[6], 2, 16, QChar('0'))
			.arg(guid.Data4[7], 2, 16, QChar('0'))
			.toUpper();
	}
#elif Q_OS_UNIX
	uuid_t u;
	uuid_generate(u);
	result.reserve(36);
	for (int i = 0; i < 16; i++) {
		result.append(QString::number(u[i], 16).rightJustified(2, '0'));
		if (i == 3 || i == 5 || i == 7 || i == 9) {
			result.append('-');
		}
	}
	result = result.toUpper();
#endif
	return result;
}

QString generate_unique_id(const QStringList& list)
{
	QByteArray hash = QCryptographicHash::hash(
		list.join("").toUtf8(),
		QCryptographicHash::Sha256);
	return hash.toHex();
}

QString save_file_to_disk(const QByteArray& data, const QString& path, const QString& name)
{
	QString file_path = path;
	if (file_path.isEmpty())
		file_path = QString("%1/%2").arg(QDir::currentPath()).arg(handle_generate_device_guid());
	if (!name.isEmpty())
		file_path = QString("%1/%2").arg(file_path).arg(name);
	QFile file(file_path);
	if (file.open(QIODevice::WriteOnly)) {
		file.write(data);
		file.close();
	}
	return file_path;
}

void handle_encrypt_file(const QString& src_file, const QByteArray& key)
{
	EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
	unsigned char iv[EVP_MAX_IV_LENGTH];
	RAND_bytes(iv, sizeof(iv));
	EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.data()), iv);

	const int buffer_size = 1024;
	unsigned char buffer[buffer_size];
	unsigned char encrypted_buffer[buffer_size + EVP_MAX_BLOCK_LENGTH];
	int bytes_read = 0;
	int bytes_encrypted = 0;

	QFile file(src_file);
	file.open(QIODevice::ReadWrite);
	QByteArray data(reinterpret_cast<const char*>(iv), AES_BLOCK_SIZE);
	while ((bytes_read = file.read(reinterpret_cast<char*>(buffer), buffer_size)) > 0) {
		EVP_EncryptUpdate(ctx, encrypted_buffer, &bytes_encrypted, buffer, bytes_read);
		data.append(reinterpret_cast<char*>(encrypted_buffer), bytes_encrypted);
	}
	EVP_EncryptFinal_ex(ctx, encrypted_buffer, &bytes_encrypted);
	data.append(reinterpret_cast<char*>(encrypted_buffer), bytes_encrypted);

	file.resize(0);
	file.write(data);
	file.close();
	EVP_CIPHER_CTX_free(ctx);
}

void handle_encrypt_file(const QString& src_file, const QString& dest_file, const QByteArray& key)
{
	EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
	unsigned char iv[EVP_MAX_IV_LENGTH];
	RAND_bytes(iv, sizeof(iv));
	EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.data()), iv);

	const int buffer_size = 1024;
	unsigned char buffer[buffer_size];
	unsigned char encrypted_buffer[buffer_size + EVP_MAX_BLOCK_LENGTH];
	int bytes_read = 0;
	int bytes_encrypted = 0;

	QFile file(src_file);
	file.open(QIODevice::ReadOnly);
	QByteArray data(reinterpret_cast<const char*>(iv), AES_BLOCK_SIZE);
	while ((bytes_read = file.read(reinterpret_cast<char*>(buffer), buffer_size)) > 0) {
		EVP_EncryptUpdate(ctx, encrypted_buffer, &bytes_encrypted, buffer, bytes_read);
		data.append(reinterpret_cast<char*>(encrypted_buffer), bytes_encrypted);
	}
	EVP_EncryptFinal_ex(ctx, encrypted_buffer, &bytes_encrypted);
	data.append(reinterpret_cast<char*>(encrypted_buffer), bytes_encrypted);
	file.close();

	QFile dest(dest_file);
	dest.open(QIODevice::WriteOnly);
	dest.write(data);
	dest.close();
	EVP_CIPHER_CTX_free(ctx);
}

void handle_decrypt_file(const QString& src_file, const QByteArray& key)
{
	EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
	QFile file(src_file);
	file.open(QIODevice::ReadWrite);

	unsigned char iv[EVP_MAX_IV_LENGTH];
	file.read(reinterpret_cast<char*>(iv), EVP_MAX_IV_LENGTH);
	EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.data()), iv);

	const int buffer_size = 1024;
	unsigned char buffer[buffer_size];
	unsigned char decrypted_buffer[buffer_size + EVP_MAX_BLOCK_LENGTH];
	int bytes_read = 0;
	int bytes_decrypted = 0;

	QByteArray data;
	while ((bytes_read = file.read(reinterpret_cast<char*>(buffer), buffer_size)) > 0) {
		EVP_DecryptUpdate(ctx, decrypted_buffer, &bytes_decrypted, buffer, bytes_read);
		data.append(reinterpret_cast<char*>(decrypted_buffer), bytes_decrypted);
	}
	EVP_DecryptFinal_ex(ctx, decrypted_buffer, &bytes_decrypted);
	data.append(reinterpret_cast<char*>(decrypted_buffer), bytes_decrypted);

	file.resize(0);
	file.write(data);
	file.close();
	EVP_CIPHER_CTX_free(ctx);
}

void handle_decrypt_file(const QString& src_file, const QString& dest_file, const QByteArray& key)
{
	EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
	QFile file(src_file);
	file.open(QIODevice::ReadOnly);

	unsigned char iv[EVP_MAX_IV_LENGTH];
	file.read(reinterpret_cast<char*>(iv), EVP_MAX_IV_LENGTH);
	EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.data()), iv);

	const int buffer_size = 1024;
	unsigned char buffer[buffer_size];
	unsigned char decrypted_buffer[buffer_size + EVP_MAX_BLOCK_LENGTH];
	int bytes_read = 0;
	int bytes_decrypted = 0;

	QByteArray data;
	while ((bytes_read = file.read(reinterpret_cast<char*>(buffer), buffer_size)) > 0) {
		EVP_DecryptUpdate(ctx, decrypted_buffer, &bytes_decrypted, buffer, bytes_read);
		data.append(reinterpret_cast<char*>(decrypted_buffer), bytes_decrypted);
	}
	EVP_DecryptFinal_ex(ctx, decrypted_buffer, &bytes_decrypted);
	data.append(reinterpret_cast<char*>(decrypted_buffer), bytes_decrypted);
	file.close();

	QFile dest(dest_file);
	dest.open(QIODevice::WriteOnly);
	dest.write(data);
	dest.close();
	EVP_CIPHER_CTX_free(ctx);
}
